package org.gdstash.db;

import org.gdstash.file.ARZRecord;
import org.gdstash.util.GDMsgFormatter;
import org.gdstash.util.GDMsgLogger;

import java.sql.*;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

public class DBFormulaSet {
    private static final String TABLE_NAME = "GD_FORMULASET";
    private static final int ROW_FORMULASET_ID = 1;
    private static ConcurrentHashMap<String, DBFormulaSet> hashBuffer;
    private String formulaSetID;
    private List<DBFormula> formulas;
    private ParameterSet parameterSet;

    public DBFormulaSet() {
        this.formulas = new LinkedList<DBFormula>();
        this.parameterSet = null;
    }

    public DBFormulaSet(final ARZRecord record) {
        this.formulaSetID = record.getFileName();
        this.formulas = record.getFormulaSetFormulaList();
        this.parameterSet = null;
    }

    static {
        DBFormulaSet.hashBuffer = new ConcurrentHashMap<String, DBFormulaSet>();
    }

    public String getFormulaSetID() {
        return this.formulaSetID;
    }

    public List<DBFormula> getFormulaList() {
        return this.formulas;
    }

    public Result getResult(final ParameterSet ps) {
        this.parameterSet = ps;
        for (final DBFormula formula : this.formulas) {
            formula.setParameterSet(this.parameterSet);
        }
        return this.getResult();
    }

    private Result getResult() {
        if (this.parameterSet == null) {
            return null;
        }
        final Result result = new Result();
        final String itemClass = this.parameterSet.getItemClass();
        if (itemClass.equals("ArmorProtective_Head")) {
            result.cost = this.getValue("armorCostEquation");
            result.physique = this.getValue("headStrengthEquation");
            result.spirit = this.getValue("headIntelligenceEquation");
        }
        if (itemClass.equals("ArmorProtective_Shoulders")) {
            result.cost = this.getValue("armorCostEquation");
            result.physique = this.getValue("shouldersStrengthEquation");
        }
        if (itemClass.equals("ArmorProtective_Chest")) {
            result.cost = this.getValue("armorCostEquation");
            result.cunning = 0.0;
            result.physique = this.getValue("chestStrengthEquation");
            result.spirit = this.getValue("chestIntelligenceEquation");
        }
        if (itemClass.equals("ArmorProtective_Hands")) {
            result.cost = this.getValue("armorCostEquation");
            result.physique = this.getValue("handsStrengthEquation");
        }
        if (itemClass.equals("ArmorProtective_Legs")) {
            result.cost = this.getValue("armorCostEquation");
            result.physique = this.getValue("legsStrengthEquation");
        }
        if (itemClass.equals("ArmorProtective_Feet")) {
            result.cost = this.getValue("armorCostEquation");
            result.physique = this.getValue("feetStrengthEquation");
        }
        if (itemClass.equals("ArmorProtective_Waist")) {
            result.cost = this.getValue("armorCostEquation");
            result.physique = this.getValue("waistStrengthEquation");
        }
        if (itemClass.equals("ArmorJewelry_Amulet")) {
            result.cost = this.getValue("jewelryCostEquation");
            result.spirit = this.getValue("amuletIntelligenceEquation");
        }
        if (itemClass.equals("ArmorJewelry_Medal")) {
            result.cost = this.getValue("jewelryCostEquation");
            result.spirit = this.getValue("amuletIntelligenceEquation");
        }
        if (itemClass.equals("ArmorJewelry_Ring")) {
            result.cost = this.getValue("jewelryCostEquation");
            result.spirit = this.getValue("ringIntelligenceEquation");
        }
        if (itemClass.equals("WeaponArmor_Offhand")) {
            result.cost = this.getValue("offhandCostEquation");
            result.spirit = this.getValue("offhandIntelligenceEquation");
        }
        if (itemClass.equals("WeaponArmor_Shield")) {
            result.cost = this.getValue("shieldCostEquation");
            result.physique = this.getValue("shieldStrengthEquation");
        }
        if (itemClass.equals("WeaponMelee_Axe")) {
            result.cost = this.getValue("weaponCostEquation");
            result.physique = this.getValue("axeStrengthEquation");
        }
        if (itemClass.equals("WeaponMelee_Mace")) {
            result.cost = this.getValue("weaponCostEquation");
            result.physique = this.getValue("maceStrengthEquation");
        }
        if (itemClass.equals("WeaponHunting_Spear")) {
            result.cost = this.getValue("weaponCostEquation");
            result.physique = this.getValue("maceStrengthEquation");
        }
        if (itemClass.equals("WeaponMelee_Sword")) {
            result.cost = this.getValue("weaponCostEquation");
            result.cunning = this.getValue("swordDexterityEquation");
        }
        if (itemClass.equals("WeaponMelee_Dagger")) {
            result.cost = this.getValue("weaponCostEquation");
            result.cunning = this.getValue("daggerDexterityEquation");
            result.spirit = this.getValue("daggerIntelligenceEquation");
        }
        if (itemClass.equals("WeaponMelee_Scepter")) {
            result.cost = this.getValue("weaponCostEquation");
            result.physique = this.getValue("scepterStrengthEquation");
            result.spirit = this.getValue("scepterIntelligenceEquation");
        }
        if (itemClass.equals("WeaponHunting_Ranged1h")) {
            result.cost = this.getValue("weaponRangedCostEquation");
            result.cunning = this.getValue("ranged1hDexterityEquation");
        }
        if (itemClass.equals("WeaponMelee_Axe2h")) {
            result.cost = this.getValue("weaponMelee2hCostEquation");
            result.physique = this.getValue("melee2hStrengthEquation");
        }
        if (itemClass.equals("WeaponMelee_Mace2h")) {
            result.cost = this.getValue("weaponMelee2hCostEquation");
            result.physique = this.getValue("melee2hStrengthEquation");
        }
        if (itemClass.equals("WeaponMelee_Spear2h")) {
            result.cost = this.getValue("weaponMelee2hCostEquation");
            result.physique = this.getValue("maceStrengthEquation");
        }
        if (itemClass.equals("WeaponMagical_Staff")) {
            result.cost = this.getValue("weaponCostEquation");
            result.cunning = this.getValue("daggerDexterityEquation");
            result.spirit = this.getValue("daggerIntelligenceEquation");
        }
        if (itemClass.equals("WeaponMelee_Sword2h")) {
            result.cost = this.getValue("weaponMelee2hCostEquation");
            result.physique = this.getValue("melee2hStrengthEquation");
        }
        if (itemClass.equals("WeaponHunting_Ranged2h")) {
            result.cost = this.getValue("weaponRanged2hCostEquation");
            result.cunning = this.getValue("ranged2hDexterityEquation");
        }
        return result;
    }

    public static void clearBuffer() {
        DBFormulaSet.hashBuffer.clear();
    }

    public static void createTables() throws SQLException {
        final String dropTable = "DROP TABLE GD_FORMULASET";
        final String createTable = "CREATE TABLE GD_FORMULASET (FORMULASET_ID      VARCHAR(256) NOT NULL, PRIMARY KEY (FORMULASET_ID))";
        try (final Connection conn = GDDBData.getConnection()) {
            final boolean auto = conn.getAutoCommit();
            conn.setAutoCommit(false);
            try (final Statement st = conn.createStatement()) {
                if (GDDBUtil.tableExists(conn, "GD_FORMULASET")) {
                    st.execute(dropTable);
                }
                st.execute(createTable);
                st.close();
                conn.commit();
            } catch (SQLException ex) {
                conn.rollback();
                final Object[] args = {"GD_FORMULASET"};
                final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_CREATE_TABLE", args);
                GDMsgLogger.addError(msg);
                throw ex;
            } finally {
                conn.setAutoCommit(auto);
            }
        }
        DBFormula.createTable();
    }

    public static void delete(final String formulaSetID) throws SQLException {
        final String deleteEntry = "DELETE FROM GD_FORMULASET WHERE FORMULASET_ID = ?";
        try (final Connection conn = GDDBData.getConnection()) {
            final boolean auto = conn.getAutoCommit();
            conn.setAutoCommit(false);
            try (final PreparedStatement ps = conn.prepareStatement(deleteEntry)) {
                ps.setString(1, formulaSetID);
                ps.executeUpdate();
                ps.close();
                DBFormula.delete(conn, formulaSetID);
                conn.commit();
            } catch (SQLException ex) {
                conn.rollback();
                final Object[] args = {formulaSetID, "GD_FORMULASET"};
                final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_DEL_TABLE_BY_ID", args);
                GDMsgLogger.addError(msg);
                GDMsgLogger.addError(ex);
                throw ex;
            } finally {
                conn.setAutoCommit(auto);
            }
        } catch (SQLException ex2) {
            throw ex2;
        }
    }

    public static void insert(final ARZRecord record) throws SQLException {
        final DBFormulaSet entry = get(record.getFileName());
        if (entry != null) {
            return;
        }
        final DBFormulaSet formulaSet = new DBFormulaSet(record);
        final String insert = "INSERT INTO GD_FORMULASET VALUES (?)";
        try (final Connection conn = GDDBData.getConnection()) {
            final boolean auto = conn.getAutoCommit();
            conn.setAutoCommit(false);
            try (final PreparedStatement ps = conn.prepareStatement(insert)) {
                ps.setString(1, record.getFileName());
                ps.executeUpdate();
                ps.close();
                conn.commit();
                DBFormula.insert(conn, formulaSet);
            } catch (SQLException ex) {
                conn.rollback();
                final Object[] args = {formulaSet.getFormulaSetID(), "GD_FORMULASET"};
                final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_INS_TABLE_BY_ID", args);
                GDMsgLogger.addError(msg);
                GDMsgLogger.addError(ex);
            } finally {
                conn.setAutoCommit(auto);
            }
        }
    }

    public static DBFormulaSet get(final String formulaSetID) {
        DBFormulaSet set = null;
        set = DBFormulaSet.hashBuffer.get(formulaSetID);
        if (set == null) {
            set = getDB(formulaSetID);
            if (set != null) {
                DBFormulaSet.hashBuffer.put(set.formulaSetID, set);
            }
        }
        return set;
    }

    private static DBFormulaSet getDB(final String formulaSetID) {
        DBFormulaSet set = null;
        final String command = "SELECT * FROM GD_FORMULASET WHERE FORMULASET_ID = ?";
        try (final Connection conn = GDDBData.getConnection();
             final PreparedStatement ps = conn.prepareStatement(command)) {
            ps.setString(1, formulaSetID);
            try (final ResultSet rs = ps.executeQuery()) {
                final List<DBFormulaSet> list = wrap(rs);
                if (list.isEmpty()) {
                    set = null;
                } else {
                    set = list.get(0);
                }
                conn.commit();
            } catch (SQLException ex) {
                throw ex;
            }
        } catch (SQLException ex2) {
            final Object[] args = {formulaSetID, "GD_FORMULASET"};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_READ_TABLE_BY_ID", args);
            GDMsgLogger.addError(msg);
            GDMsgLogger.addError(ex2);
        }
        return set;
    }

    public static List<DBFormulaSet> getByFormulaSetIDs(final List<String> formulaSetIDs) {
        final List<DBFormulaSet> list = new LinkedList<DBFormulaSet>();
        for (final String formulaSetID : formulaSetIDs) {
            final DBFormulaSet set = get(formulaSetID);
            if (set != null) {
                list.add(set);
            }
        }
        return list;
    }

    private static List<DBFormulaSet> wrap(final ResultSet rs) throws SQLException {
        final LinkedList<DBFormulaSet> list = new LinkedList<DBFormulaSet>();
        while (rs.next()) {
            final DBFormulaSet set = new DBFormulaSet();
            set.formulaSetID = rs.getString(1);
            if (set.formulaSetID != null) {
                set.formulas = DBFormula.getByFormulaSetID(set.formulaSetID);
            }
            list.add(set);
        }
        return list;
    }

    private double getValue(final String formulaID) {
        double value = 0.0;
        for (final DBFormula formula : this.formulas) {
            if (formula.getFormulaID().equals(formulaID)) {
                try {
                    value = formula.getValue();
                } catch (IllegalStateException ex) {
                    value = 0.0;
                }
                break;
            }
        }
        return value;
    }

    public static class Result {
        public double cost;
        public double cunning;
        public double physique;
        public double spirit;

        public Result() {
            this.cost = 0.0;
            this.cunning = 0.0;
            this.physique = 0.0;
            this.spirit = 0.0;
        }
    }
}
